package org.xxpay.pay.channel.alipay;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.domain.AlipayTradeCreateModel;
import com.alipay.api.request.AlipayTradeCreateRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.xxpay.core.common.constant.MchConstant;
import org.xxpay.core.common.constant.PayConstant;
import org.xxpay.core.common.constant.RetEnum;
import org.xxpay.core.common.exception.ServiceException;
import org.xxpay.core.common.util.AmountUtil;
import org.xxpay.core.common.util.MyBase64;
import org.xxpay.core.common.util.MyLog;
import org.xxpay.core.common.vo.DBApplicationConfig;
import org.xxpay.core.entity.PayOrder;
import org.xxpay.core.entity.PayPassageAccount;
import org.xxpay.pay.ctrl.common.BaseController;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.URLEncoder;

@Controller
public class AlipayController extends BaseController {

    private static final MyLog _log = MyLog.getLog(AlipayController.class);

    /** 当前线程变量，用于判断当前是否是测试状态
     由于getPayParam()函数中判断了子账号的状态, 当MS新增后状态是非开启，也需要进行接口验证，所以添加该线程变量；
     另外商户下单时前置判断条件已经验证了该账号状态，此时判断状态感觉有点重复（需确认）；
     **/
    public static final ThreadLocal<String> IS_TEST_FLAG = new ThreadLocal<>();

    @RequestMapping("/api/alipay/pay_{type}.htm")
    public String toPay(HttpServletRequest request, ModelMap model, @PathVariable("type") String type) throws ServletException, IOException {
        JSONObject po = getJsonParam(request);
        // https://qr.alipay.com/fkx04112akzpulwo9pvye74?t=1541681073413
        String amount = getString(po, "amount");
        String payOrderId = getString(po, "payOrderId");
        String mchOrderNo = getString(po, "mchOrderNo");
        String expireTime = String.valueOf(5*60);
        String toUrl = getString(po, "toUrl");
        String codeUrl = getString(po, "codeUrl");
        String codeImgUrl = getString(po, "codeImgUrl");
//        String toAlipayUrl = "https://mobilecodec.alipay.com/client_download.htm?qrcode=" + codeUrl.substring(codeUrl.lastIndexOf("/") + 1);
        String startAppUrlH5 = "";
        if (StringUtils.isNotBlank(toUrl)){
            startAppUrlH5 = "alipays://platformapi/startapp?saId=10000007&clientVersion=3.7.0.0718&qrcode=" + URLEncoder.encode(toUrl);
        }
        String startAppUrl = codeUrl;

        // https://mobilecodec.alipay.com/client_download.htm?qrcode=fkx04319ftj9wcl46eh8m5a&t=1540812174754
        //String toAlipayUrl = "https://mobilecodec.alipay.com/client_download.htm?qrcode=" + codeUrl.substring(codeUrl.lastIndexOf("/") + 1);
        // alipays://platformapi/startapp?saId=10000007&clientVersion=3.7.0.0718&qrcode=https%3a%2f%2fmobilecodec.alipay.com%2fclient_download.htm%3fqrcode%3dfkx04112akzpulwo9pvye74%3ft%3d1541681073413
        //String startAppUrl = "alipays://platformapi/startapp?saId=10000007&clientVersion=3.7.0.0718&qrcode=" + URLEncoder.encode(toAlipayUrl);
        model.put("amount", AmountUtil.convertCent2Dollar(amount));
        model.put("amountStr", "￥"+ AmountUtil.convertCent2Dollar(amount));
        model.put("mchOrderNo", mchOrderNo);
        model.put("payOrderId", payOrderId);
        model.put("expireTime", expireTime);
        model.put("codeUrl", codeUrl);
        model.put("codeImgUrl", codeImgUrl);
//        model.put("toAlipayUrl", toAlipayUrl);
        model.put("startAppUrl", startAppUrl);
        if (StringUtils.isNotBlank(startAppUrlH5)) {
            model.put("startAppUrlH5", startAppUrlH5);
        }
        String requestHeader = request.getHeader("user-agent");
        boolean isMobile = isMobileDevice(requestHeader);
        model.put("isMobile", isMobile);    // 是否手机浏览器访问

        return "payment/alipay/pay_" + type;
    }

    @RequestMapping("/api/alipay/alipay_qr_{param}.htm")
    public String alipayRec(HttpServletRequest request, ModelMap model, @PathVariable String param) {
        String logPrefix = "【支付宝当面付之H5支付下单】";
        String paramJson = new String(MyBase64.decode(param));
        JSONObject po = JSONObject.parseObject(paramJson);
        String payOrderId = getString(po, "payOrderId");
        String buyerId = request.getParameter("userId");
        if(buyerId == null) {
            _log.error("{}支付宝授权失败", logPrefix);
            model.put("errMsg","授权失败，请重新下单");
            return "payment/alipay/alipay_qr";
        }

        //获取订单信息
        PayOrder payOrder = commonService.payOrderService.findByPayOrderId(payOrderId);

        AlipayConfig alipayConfig = new AlipayConfig(getPayParam(payOrder));

        AlipayTradeCreateRequest alipay_request = new AlipayTradeCreateRequest();
        AlipayTradeCreateModel alipayModel = new AlipayTradeCreateModel();
        alipayModel.setOutTradeNo(payOrderId);
        alipayModel.setSubject(payOrder.getSubject());
        alipayModel.setTotalAmount(AmountUtil.convertCent2Dollar(payOrder.getAmount().toString()));
        alipayModel.setBody(payOrder.getBody());
        alipayModel.setBuyerId(buyerId);
        // 获取objParams参数
        String objParams = payOrder.getExtra();
        if (StringUtils.isNotEmpty(objParams)) {
            try {
                JSONObject objParamsJson = JSON.parseObject(objParams);
                if(StringUtils.isNotBlank(objParamsJson.getString("discountable_amount"))) {
                    //可打折金额
                    alipayModel.setDiscountableAmount(objParamsJson.getString("discountable_amount"));
                }
                if(StringUtils.isNotBlank(objParamsJson.getString("undiscountable_amount"))) {
                    //不可打折金额
                    alipayModel.setUndiscountableAmount(objParamsJson.getString("undiscountable_amount"));
                }
            } catch (Exception e) {
                _log.error("{}objParams参数格式错误{}", logPrefix, objParams);
                //model.put("errMsg","参数错误，请重新下单");
                //return "payment/alipay/alipay_qr";
            }
        }
        // 配置信息
        DBApplicationConfig config = commonService.sysConfigService.getDBApplicationConfig();

        alipay_request.setBizModel(alipayModel);
        // 设置异步通知地址
        alipay_request.setNotifyUrl(super.getNotifyUrl(PayConstant.CHANNEL_NAME_ALIPAY, config));
        // 设置同步跳转地址
        alipay_request.setReturnUrl(super.getReturnUrl(PayConstant.CHANNEL_NAME_ALIPAY, config));
        String aliResult;
        String tradeNo = "";
        try {
            aliResult = AlipayConfig.alipayExecute(alipayConfig, alipay_request).getBody();
            JSONObject aliObj = JSONObject.parseObject(aliResult);
            JSONObject aliResObj = aliObj.getJSONObject("alipay_trade_create_response");
            tradeNo = aliResObj.getString("trade_no");
        } catch (AlipayApiException e) {
            _log.error(e, "获取订单失败[" + e.getErrMsg() + "]");
            model.put("errMsg", "下单失败，请重新下单");
            return "payment/alipay/alipay_qr";
        } catch (Exception e) {
            _log.error(e, "获取订单失败[调取通道异常]");
            model.put("errMsg", "下单失败，请重新下单");
            return "payment/alipay/alipay_qr";
        }

        _log.info("{}生成支付宝交易单号：tradeNo={}", logPrefix, tradeNo);
        if (StringUtils.isEmpty(tradeNo)) {
            model.put("errMsg", "下单失败，请重新下单");
            return "payment/alipay/alipay_qr";
        }

        int updateCount = commonService.payOrderService.updateStatus4Ing(payOrderId, tradeNo);
        _log.info("[{}]更新订单状态为支付中:payOrderId={},result={}", logPrefix, payOrderId, updateCount);

        model.put("tradeNo", tradeNo);
        return "payment/alipay/alipay_qr";
    }

    /**
     * 订单查询
     * @param request
     * @return
     * @throws ServletException
     * @throws IOException
     */
    @RequestMapping("/api/alipay/query")
    @ResponseBody
    public String toQuery(HttpServletRequest request) throws ServletException, IOException {
        JSONObject po = getJsonParam(request);
        String payOrderId = getString(po, "payOrderId");
        _log.info("[查询订单]参数payOrderId={}", payOrderId);

        PayOrder payOrder = commonService.payOrderService.findByPayOrderId(payOrderId);
        JSONObject retObj = new JSONObject();
        String status = "-1";
        String url = "";
        if(payOrder != null) {
            status = payOrder.getStatus() + "";
            url = payOrder.getReturnUrl();
        }
        retObj.put("status", status);
        retObj.put("url", url);
        _log.info("[查询订单]结果payOrderId={},retObj={}", payOrderId, retObj);
        return retObj.toJSONString();
    }

    public static boolean  isMobileDevice(String requestHeader){
        /**
         * android : 所有android设备
         * ios : iphone ipad
         * windows phone:Nokia等windows系统的手机
         */
        String[] deviceArray = new String[]{"android","iphone","ipad","windows phone"};
        if(requestHeader == null)
            return false;
        requestHeader = requestHeader.toLowerCase();
        for(int i=0;i<deviceArray.length;i++){
            if(requestHeader.indexOf(deviceArray[i])>0){
                return true;
            }
        }
        return false;
    }

    /**
     * 获取三方支付配置信息
     * 如果是平台账户,则使用平台对应的配置,否则使用商户自己配置的渠道
     * @param payOrder
     * @return
     */
    public String getPayParam(PayOrder payOrder) {
        String payParam = "";
        PayPassageAccount payPassageAccount = commonService.payPassageAccountService.findById(payOrder.getPassageAccountId());

        if(payPassageAccount == null) throw new ServiceException(RetEnum.RET_MGR_PAY_PASSAGE_ACCOUNT_NOT_EXIST);
        if(StringUtils.isBlank(IS_TEST_FLAG.get()) && payPassageAccount.getStatus() != MchConstant.PUB_YES){ // 空=非测试，需要判断状态是否开启
            throw new ServiceException(RetEnum.RET_MGR_PAY_PASSAGE_ACCOUNT_NOT_EXIST);
        }

        payParam = payPassageAccount.getParam();
        if(StringUtils.isBlank(payParam)) throw new ServiceException(RetEnum.RET_MGR_PAY_PASSAGE_ACCOUNT_NOT_EXIST); //仍然为空，说明配置有误

        return payParam;
    }
}
